﻿using Dispatcher.Tasks.Models;
using Dispatcher.Tasks.Providers;
using MiniFox.Components;
using MiniFox.Infrastructure;
using MiniFox.Net.Http;
using MiniFox.Platform;
using MiniFox.Platform.Applications;
using MiniFox.Platform.Applications.Auth;
using MiniFox.Platform.File.Models;
using ApplicationIdentity = MiniFox.Platform.ApplicationIdentity;

namespace Dispatcher.Tasks
{
    [AutoRegister]
    public abstract class QueueService : ApiServiceBase
    {
        internal static readonly int queueBatchCount;
        internal static readonly int retryTimes;

        static QueueService()
        {
            queueBatchCount = 100;
            retryTimes = 3;
        }

        public QueueService(SystemPrincipal principal) : base(principal)
        {
        }
        [AutoWired]
        public IQueueDataProvider Provider { get; set; }

        protected internal virtual void Push(string queueName, string executionId, object data, object query)
        {
            var httpAdapter = CreateAdapter();
            var accessToken = this.Principal.AuthorizeToken.AccessToken;
            var identity = this.Principal.Identity as ApplicationIdentity;
            var httpService = httpAdapter.GetService(HttpServiceName);
            var headers = new { Authorization = $"{OpenAuthKey.AUTH_SCHEME} {accessToken}" };
            var queueItem = QueueFactory.CreateQueueItem(queueName, executionId, httpService);
            queueItem.Identity = identity;
            queueItem.AccessToken = accessToken;
            queueItem.CreationTime = DateTimeOffset.Now;
            queueItem.RetryCount = 0;
            queueItem.OrignalData = data;
            queueItem.Query = query;
            queueItem.Headers = headers;
            if (this.Provider.CreateQueueItem(queueItem))
            {
                var queue = QueueFactory.LoadQueue(queueName);
                queue.Enqueue(queueItem);
            }

        }

        protected internal Task<Response<FileData[]>> SetAttachmentsAsync(string accessToken, string contextId, IEnumerable<FileData> fileDatas)
        {
            var files = fileDatas.Select(f => (f.FormName, f.FileName, f.ContentType, f.Data));
            return SetAttachmentsAsync(accessToken, contextId, files);
        }

        protected internal async virtual Task<Response<FileData[]>> SetAttachmentsAsync(string accessToken, string contextId, IEnumerable<(string formName, string fileName, string mimeType, byte[] data)> files)
        {
            var httpAdapter = CreateAdapter();
            var attachService = httpAdapter.GetService(HttpAttachServiceName);

            var headers = new { Authorization = $"{OpenAuthKey.AUTH_SCHEME} {accessToken}" };
            var query = new { contextId };

            var response = await attachService.SendAsync<Response<FileData[]>>(files, query, headers);

            return response;
        }

        private void Log(QueueItem queueItem, Response response)
        {
            if (!response.Success && (queueItem.RetryCount++ < retryTimes))
            {
                this.Provider.RetryCounting(queueItem);
                var queue = QueueFactory.LoadQueue(queueItem.QueueName);
                queue.Enqueue(queueItem);
                return;
            }
            //logging
            this.Provider.ExectionLog(queueItem, response);
        }

        internal async Task PullAsync(string queueName)
        {
            var queue = QueueFactory.LoadQueue(queueName);
            while (queue.Count > 0)
            {
                var queueItem = queue.Dequeue();
                try
                {
                    var response = await queueItem.ExecuteAsync();
                    Log(queueItem, response);
                }
                catch (Exception e)
                {
                    if (queueItem != null)
                    {
                        //error logging
                        this.Provider.FatalLog(queueItem, e);
                    }
                    else
                    {
                        this.WriteErrorLog(e.Message, e);
                    }
                }
            }
        }
        protected abstract string HttpAdapterName { get; }
        protected abstract string HttpServiceName { get; }

        protected abstract string HttpAttachServiceName { get; }

        protected HttpAdapter CreateAdapter()
        {
            return QueueFactory.CreateAdapter(HttpAdapterName);
        }

    }
}
